/*
 * @Author: tanjunyi@doctorwork.com
 * @Date: 2020-04-02 22:13:22
 * @LastEditors: tanjunyi@doctorwork.com
 * @LastEditTime: 2020-04-08 23:09:09
 * @Description:
 */
const fs = require('fs');
const path = require('path');
const parser = require('@babel/parser');
const babel = require('@babel/core');
const traverse = require('@babel/traverse').default;
const Webpack = function (defaultConfig) {
	this._plugins = {};
	this.compiler = {
		// 注册各个插件
		plugin: (type, callback) => {
			if (Array.isArray(this._plugins[type])) {
				this._plugins[type].push(callback)
			} else {
				this._plugins[type] = [callback]
			}
		},
		// 触发执行
		apply: (type, callback) => {
			this.applyPluginsAsync(type, this.compilation, callback);
		}
	}
	this.compilation = {
		assets: []
	}
	this.config = defaultConfig;
	this.sourcePath = defaultConfig.entry;
	this.pluginList = [];
};

Webpack.prototype.moduleAnalyser = function (filename) {
	var content = fs.readFileSync(filename, 'utf-8');
	// * 注册loaders
	// loader在别名之前执行还是之后执行？
	const loadRules = this.config.module.rules;
	loadRules.map(rule => {
		// 优先处理loader逻辑
		if (rule.test.test(filename) && !rule.exclude.test(filename)) {
			// . 判断当前loader类型
			if (Array.isArray(rule.use)) {
				let uses = rule.use;
				uses.forEach(use => {
					// . 自定义loader
					if (typeof use.loader == 'function') {
						content = use.loader(content, use.options)
					} else if (typeof use.loader == 'string') {
						// var makeLoader = require(use.loader);
						// this.async = () => {
						// 	console.log("执行callback")
						// }
						// var loader = makeLoader.bind(this, () => {
						// 	console.log("执行创建loader回调")
						// });
						// content = loader(content, use.options)
					}

				})
			}
		}
	})


	var ast = parser.parse(content, {
		sourceType: 'module'
	})
	const dependences = {};
	traverse(ast, {
		ClassMethod({ node }) {
			console.log(node)
		},
		ImportDeclaration({ node }) {
			console.log(node)
			const dirname = path.dirname(filename);
			let newFile;
			if (~node.source.value.indexOf('@')) {
				newFile = node.source.value.replace('@', 'src');
			} else {
				newFile = path.join(dirname, node.source.value)
			}
			dependences[node.source.value] = newFile;
		}

	})
	console.log('dependences:', dependences);
	const code = babel.transformFromAst(ast, null, {
		presets: ['@babel/preset-env']
	}).code
	return {
		filename,
		dependences,
		code
	}
}
/**
 * 生成依赖关系树
 * @param {*} entry 
 */
Webpack.prototype.makeDependencesGraph = function (entry) {
	const entryModule = this.moduleAnalyser(entry);
	const graphArray = [entryModule];
	for (let i = 0; i < graphArray.length; i++) {
		let item = graphArray[i];
		const { dependences } = item;
		if (dependences) {
			for (let j in dependences) {
				graphArray.push(this.moduleAnalyser(dependences[j]))
			}
		}
	}

	let graph = {};
	graphArray.forEach(item => {
		graph[item.filename] = {
			dependences: item.dependences,
			code: item.code
		}
	})
	return graph;

}
// -------------------- 1. 注册入口 -------------------------------
/**
 * 读取入口文件
 * @param {*} config 
 */
Webpack.prototype.readEntry = function (config) {
	const source = this.makeDependencesGraph(config.entry);
	console.log('#### source:', JSON.stringify(source))
	return source;
}



// -------------------- 2. 注册loader -------------------------------

Webpack.prototype.registLoaders = function () {

}

// -------------------- 3. 注册plugin -------------------------------
Webpack.prototype.registPlugins = function () {
	this.config.plugins.forEach(plugin => {
		plugin.apply(this.compiler);
	})
}

Webpack.prototype.applyPlugins = function () {

}


Webpack.prototype.applyPluginsAsync = function (type, compilation, callback) {
	var plugin = this._plugins[type];
	function excute() {
		var currentFn = plugin.shift();
		if (currentFn) {
			currentFn(compilation, excute);
		}
	}
	excute(compilation, plugin);
}

// -------------------- 4. 最终输出 -------------------------------

/**
 * 生成代码
 * @param {*} code 
 */
Webpack.prototype.generateCode = function (entry, code) {
	const codeStr = JSON.stringify(code);
	return `(function(graph){
		function require(module){
			function localRequire(relativePath){
				return require(graph[module].dependences[relativePath])
			}
			var exports = {};	
			(function(require,exports,code){
				eval(code);
			})(localRequire, exports,graph[module].code);
			return exports;
		}
		require('${entry}');
	})(${codeStr})`;
}

// -------------------- 5. 生成文件 -------------------------------

Webpack.prototype.writeFile = function () {
	const { assets } = this.compilation;

	for (filename in assets) {
		let filepath = path.resolve(this.config.output.path, filename);
		let content = assets[filename].source;
		if (typeof content == 'function') {
			content = content();
		}
		fs.writeFileSync(filepath, content)
	}
}

Webpack.prototype.run = function () {
	// * 注册配置文件
	this.registPlugins();
	// * 开始分析入口文件
	let source = this.readEntry(this.config);
	// * 注入辅助代码
	let code = this.generateCode(this.config.entry, source)
	const { filename } = this.config.output;
	this.compilation.assets[filename] = {
		source: code,
		length: code.length
	};
	// * 执行plugin的compilation阶段
	this.compiler.apply('compilation', () => { console.log('plugin compilation执行完毕') })
	// * 输出文件
	this.writeFile();
};

module.exports = Webpack;
